﻿namespace MonetDbDataProvider
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;

    using MonetDbDataProvider.Query;

    public class MonetDbDataReader : IDataReader
    {
        internal IList<QueryResult> queryResults { get; set; }

        internal IDbCommand command { get; set; }

        internal int currentRow { get; set; }

        internal int currentResultSetIndex { get; set; }

        internal MonetDbDataReader(IList<QueryResult> data, IDbCommand command)
        {
            this.command = command;
            this.IsClosed = command.Connection.State == ConnectionState.Closed;
            this.currentRow = -1;

            this.queryResults = data;
            this.FieldCount = (data[this.currentResultSetIndex].Data != null) ? data[this.currentResultSetIndex].Data.Columns.Count : 0;
        }

        public void Dispose()
        {
            this.Close();
        }

        public string GetName(int i)
        {
            return this.queryResults[this.currentResultSetIndex].Data.Columns[i].ColumnName;
        }

        public string GetDataTypeName(int i)
        {
            throw new NotImplementedException();
        }

        public Type GetFieldType(int i)
        {
            return this.queryResults[this.currentResultSetIndex].Data.Columns[i].DataType;
        }

        public object GetValue(int i)
        {
            return this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i];
        }

        public int GetValues(object[] values)
        {
            var returnSize = (values.Length >= this.queryResults[this.currentResultSetIndex].Data.Columns.Count)
                                 ? this.queryResults[this.currentResultSetIndex].Data.Rows.Count
                                 : values.Length;

            for (var i = 0; i < returnSize; i++)
                values[i] = this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i];

            return returnSize;
        }

        public int GetOrdinal(string name)
        {
            return
                this.queryResults[this.currentResultSetIndex].Data.Columns.Cast<DataColumn>()
                    .Where(column => column.ColumnName == name)
                    .Select(column => column.Ordinal)
                    .FirstOrDefault();
        }

        public bool GetBoolean(int i)
        {
            return bool.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public byte GetByte(int i)
        {
            return byte.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
        {
            throw new NotImplementedException();
        }

        public char GetChar(int i)
        {
            return (char)this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i];
        }

        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
        {
            throw new NotImplementedException();
        }

        public Guid GetGuid(int i)
        {
            return Guid.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public short GetInt16(int i)
        {
            return short.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public int GetInt32(int i)
        {
            return int.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public long GetInt64(int i)
        {
            return long.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public float GetFloat(int i)
        {
            return float.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public double GetDouble(int i)
        {
            return double.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public string GetString(int i)
        {
            return Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]);
        }

        public decimal GetDecimal(int i)
        {
            return decimal.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public DateTime GetDateTime(int i)
        {
            return DateTime.Parse(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        public IDataReader GetData(int i)
        {
            throw new NotImplementedException();
        }

        public bool IsDBNull(int i)
        {
            return string.IsNullOrEmpty(Convert.ToString(this.queryResults[this.currentResultSetIndex].Data.Rows[this.currentRow][i]));
        }

        object IDataRecord.this[int i]
        {
            get
            {
                return this.GetValue(i);
            }
        }

        object IDataRecord.this[string name]
        {
            get
            {
                return this.GetValue(this.GetOrdinal(name));
            }
        }

        public void Close()
        {
            this.command.Dispose();
            this.IsClosed = this.command.Connection.State == ConnectionState.Closed;
        }

        public DataTable GetSchemaTable()
        {
            throw new NotImplementedException();
        }

        public bool NextResult()
        {
            bool results;

            if ((this.currentResultSetIndex + 1) < this.queryResults.Count)
            {
                this.currentResultSetIndex++;
                this.currentRow = -1;
                results = true;
            }
            else results = false;

            return results;
        }

        public bool Read()
        {
            bool results;

            if (this.queryResults[this.currentResultSetIndex].Data == null) return false;

            if ((this.currentRow + 1) < this.queryResults[this.currentResultSetIndex].Data.Rows.Count)
            {
                this.currentRow++;
                results = true;
            }
            else results = false;

            return results;
        }

        public int FieldCount { get; private set; }

        public int Depth { get; private set; }

        public bool IsClosed { get; private set; }

        public int RecordsAffected { get; private set; }
    }
}
